// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once

#include "GameFeatureAction_WorldActionBase.h"
#include "UObject/SoftObjectPtr.h"
#include "GameFeatureAction_AddInputContextMapping.generated.h"

class AActor;
class UInputMappingContext;
class UPlayer;
class APlayerController;
struct FComponentRequestHandle;

/**
 * 输入映射上下文和优先级结构体
 */
USTRUCT()
struct FInputMappingContextAndPriority
{
	GENERATED_BODY()

	UPROPERTY(EditAnywhere, Category="Input", meta=(AssetBundles="Client,Server"))
	TSoftObjectPtr<UInputMappingContext> InputMapping; // 输入映射上下文

	// 更高优先级的输入映射将优先于较低优先级的映射
	UPROPERTY(EditAnywhere, Category="Input")
	int32 Priority = 0; // 优先级
	
	/** 如果为true，则此映射上下文将在注册此游戏特性操作时注册到设置中 */
	UPROPERTY(EditAnywhere, Category="Input")
	bool bRegisterWithSettings = true; // 是否注册到设置
};

/**
 * 将InputMappingContext添加到本地玩家的EnhancedInput系统
 * 期望本地玩家已设置为使用EnhancedInput系统
 */
UCLASS(MinimalAPI, meta = (DisplayName = "Add Input Mapping"))
class UGameFeatureAction_AddInputContextMapping final : public UGameFeatureAction_WorldActionBase
{
	GENERATED_BODY()

public:
	//~UGameFeatureAction 接口
	virtual void OnGameFeatureRegistering() override; // 游戏特性注册时调用
	virtual void OnGameFeatureActivating(FGameFeatureActivatingContext& Context) override; // 游戏特性激活时调用
	virtual void OnGameFeatureDeactivating(FGameFeatureDeactivatingContext& Context) override; // 游戏特性停用时调用
	virtual void OnGameFeatureUnregistering() override; // 游戏特性取消注册时调用
	//~UGameFeatureAction 接口结束

	//~UObject 接口
#if WITH_EDITOR
	virtual EDataValidationResult IsDataValid(class FDataValidationContext& Context) const override; // 数据验证
#endif
	//~UObject 接口结束

	UPROPERTY(EditAnywhere, Category="Input")
	TArray<FInputMappingContextAndPriority> InputMappings; // 输入映射列表

private:
	/**
	 * 每个上下文的数据结构
	 */
	struct FPerContextData
	{
		TArray<TSharedPtr<FComponentRequestHandle>> ExtensionRequestHandles; // 扩展请求句柄数组
		TArray<TWeakObjectPtr<APlayerController>> ControllersAddedTo; // 已添加到的控制器数组
	};

	TMap<FGameFeatureStateChangeContext, FPerContextData> ContextData; // 上下文数据映射
	
	/** 游戏实例更改时注册IMC的委托 */
	FDelegateHandle RegisterInputContextMappingsForGameInstanceHandle; // 注册输入上下文映射的委托句柄

	/** 将拥有的输入映射上下文注册到输入注册表子系统。同时绑定到游戏实例的开始和本地玩家的添加/移除 */
	void RegisterInputMappingContexts(); // 注册输入映射上下文
	
	/** 将拥有的输入映射上下文注册到指定游戏实例的输入注册表子系统。这也会在游戏实例开始时被调用 */
	void RegisterInputContextMappingsForGameInstance(UGameInstance* GameInstance); // 为游戏实例注册输入上下文映射

	/** 将拥有的输入映射上下文注册到指定本地玩家的输入注册表子系统。这也会在添加本地玩家时被调用 */
	void RegisterInputMappingContextsForLocalPlayer(ULocalPlayer* LocalPlayer); // 为本地玩家注册输入映射上下文

	/** 从输入注册表子系统取消注册拥有的输入映射上下文。同时取消绑定游戏实例的开始和本地玩家的添加/移除 */
	void UnregisterInputMappingContexts(); // 取消注册输入映射上下文

	/** 从输入注册表子系统取消注册指定游戏实例的输入映射上下文 */
	void UnregisterInputContextMappingsForGameInstance(UGameInstance* GameInstance); // 为游戏实例取消注册输入上下文映射

	/** 从输入注册表子系统取消注册指定本地玩家的输入映射上下文。这也会在移除本地玩家时被调用 */
	void UnregisterInputMappingContextsForLocalPlayer(ULocalPlayer* LocalPlayer); // 为本地玩家取消注册输入映射上下文

	//~UGameFeatureAction_WorldActionBase 接口
	virtual void AddToWorld(const FWorldContext& WorldContext, const FGameFeatureStateChangeContext& ChangeContext) override; // 添加到世界
	//~UGameFeatureAction_WorldActionBase 接口结束

	void Reset(FPerContextData& ActiveData); // 重置数据
	void HandleControllerExtension(AActor* Actor, FName EventName, FGameFeatureStateChangeContext ChangeContext); // 处理控制器扩展
	void AddInputMappingForPlayer(UPlayer* Player, FPerContextData& ActiveData); // 为玩家添加输入映射
	void RemoveInputMapping(APlayerController* PlayerController, FPerContextData& ActiveData); // 移除输入映射
};